﻿using Avalonia.Controls;
using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Runtime;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.Drawing
{
    public class RayAction : ElementAction
    {
        public static LcCreateMethod[] CreateMethods;

        static RayAction()
        {
            CreateMethods = new LcCreateMethod[1];
            CreateMethods[0] = new LcCreateMethod()
            {
                Name = "2P",
                Description = "两点创建射线",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name=  "Step0", Options= "指定起点：" },
                    new LcCreateStep { Name=  "Step1", Options= "指定通过点：" },
                    new LcCreateStep { Name=  "Step2", Options= "指定通过点：" },
                }
            };
        }

        internal static void Initilize()
        {
            ElementActions.Ray = new RayAction();
            LcDocument.ElementActions.Add(BuiltinElementType.Ray, ElementActions.Ray);
        }

        private List<Vector2d> points;
        private Vector2d? originPoint { get; set; }
        private Vector2d? thruPoint { get; set; }
        private PointInputer inputer { get; set; }

        private RayAction() { }
        public RayAction(DocumentRuntime docRt) : base(docRt)
        {
            docRt.CommandCtrl.WriteInfo("命令：Ray");
        }

        public async void ExecCreate(string[] args = null)
        {
            this.points = new List<Vector2d>();
            this.StartCreating();
            this.inputer = new PointInputer(this.docRt);
            var curMethod = CreateMethods[0];
        Step0:
            var step0 = curMethod.Steps[0];
            var result0 = await inputer.Execute(step0.Options);

            if (inputer.isCancelled)
            {
                this.Cancel();
                return;
            }

            if (result0.Value == null)
            {
                if (result0.Option != null)
                {

                }
                else
                {
                    goto Step1;
                }
            }
            this.originPoint = (Vector2d)result0.Value;
        Step1:
            var step1 = curMethod.Steps[1];
            if (this.points.Count >= 2)
            {
                step1 = curMethod.Steps[2];
            }
            var result1 = await inputer.Execute(step1.Options);
            if (inputer.isCancelled)
            {
                this.Cancel();
                return;
            }
            if (result1.Value == null)
            {
                if (result1.Option == " ")
                {
                    goto End;
                }
                else if (result1.Option != null)
                {
                    docRt.CommandCtrl.WriteInfo(SR.PointError);
                    goto Step1;
                }
                else
                {
                    goto Step1;
                }
            }

            this.thruPoint = (Vector2d)result1.Value;
            points.Add(thruPoint.Value);
            goto Step1;
        End:
            this.inputer = null;
            this.EndCreating();
            if (this.points.Count > 0)
            {
                this.CreateElements();
            }
        }

        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
            this.originPoint = null;
            this.thruPoint = null;
            this.EndCreating();
        }

        public void CreateElements()
        {
            if (this.originPoint != null)
            {
                for (var i = 0; i < this.points.Count; i++)
                {
                    var p = this.points[i];
                    this.docRt.Document.ModelSpace.AddRay(this.originPoint.Value, p);
                }
            }
        }

        public override void DrawAuxLines(SKCanvas canvas)
        {
            if (originPoint != null)
            {
                var mp = this.vportRt.PointerMovedPosition.ToVector2d();
                var wcs_mp = this.vportRt.ConvertScrToWcs(mp);
                DrawRay(canvas, originPoint.Value, wcs_mp);
            }
        }

        public override void DrawTemp(SKCanvas canvas)
        {
            if (this.originPoint != null)
            {
                for (var i = 0; i < this.points.Count; i++)
                {
                    var p = this.points[i];
                    DrawRay(canvas, this.originPoint.Value, p);
                }
            }
        }
        private void DrawRay(SKCanvas canvas, Vector2d p0, Vector2d p1)
        {
            if (p0.Equals(p1))
                return;

            var box = this.vportRt.Renderer.GetWcsClipBox();

            var rayDir = (p1 - p0).Normalized;
            var ray = new LcRay(p0, rayDir);
            var interPs = GeoUtils.IntersectRayBox(ray, box);

            var interP = interPs.OrderByDescending(P => Vector2d.Distance(p0, P)).FirstOrDefault();

            var sk_pre = this.vportRt.ConvertWcsToScr(p0).ToSKPoint();
            var sk_p = this.vportRt.ConvertWcsToScr(interP).ToSKPoint();
            //get Layer color 
            using (var paint = new SKPaint { Color = SKColors.Red })
            {
                canvas.DrawLine(sk_pre, sk_p, paint);
            }
        }

        public override void Draw(SKCanvas canvas, LcElement element, Matrix3d matrix)
        {
            var ray = element as LcRay;
            var box = this.vportRt.Renderer.GetWcsClipBox();
            var interPs =  GeoUtils.IntersectRayBox(ray, box);
            var interP = interPs.OrderByDescending(P => Vector2d.Distance(ray.StartPoint, P)).FirstOrDefault();

            var mstart = matrix.MultiplyPoint(ray.StartPoint);
            var mend = matrix.MultiplyPoint(interP);

            var start = this.vportRt.ConvertWcsToScr(mstart).ToSKPoint();
            var end = this.vportRt.ConvertWcsToScr(mend).ToSKPoint();

            bool isDragCopy = (matrix != Matrix3d.Zero);
            var pen = this.GetDrawPen(ray, isDragCopy);

            canvas.DrawLine(start, end, pen);
        }
        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var ray = element as LcRay;
            var grips = new List<ControlGrip>();
            var gripStart = new ControlGrip
            {
                Element = ray,
                Name = "Start",
                Position = ray.StartPoint
            };
            grips.Add(gripStart);
            //var gripEnd = new ControlGrip
            //{
            //    Element = ray,
            //    Name = "End",
            //    Position = ray.End
            //};
            //grips.Add(gripEnd);
            return grips.ToArray();
        }

        private string _gripName;
        private Vector2d _position;
        private LcRay _ray;
        public override void SetDragGrip(LcElement element, string gripName, Vector2d position, bool isEnd)
        {
            var ray = element as LcRay;
            _ray = ray;
            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
            }
            else
            {
                if (gripName == "Start")
                    ray.Set(startPoint: position);
                else if (gripName == "End")
                {
                    _ray.Set(dir: position);
                }
            }
        }

        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_ray == null) return;

            var start = this.vportRt.ConvertWcsToScr(_ray.StartPoint);
            if (_gripName == "Start")
                start = this.vportRt.ConvertWcsToScr(_position);

            //var end = this.vportRt.ConvertWcsToScr(_ray.End);
            //if (_gripName == "End")
            //    end = this.vportRt.ConvertWcsToScr(_position);

            //canvas.DrawLine(start.ToSKPoint(), end.ToSKPoint(), Constants.draggingPen);
        }
    }
}
